
using EmperialApps.WeatherSpark.Data;
using EmperialApps.WeatherSpark.Service.Internal;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsPhone.Samples.Notifications;
using System;
using System.Diagnostics;
using System.Text;

namespace EmperialApps.WeatherSpark.Service {

    public class WebRole : RoleEntryPoint {

        private ForecastManager _manager;
        private LogManager _logManager;


        public override bool OnStart( ) {
            try {
                string logDirectory = AzureLocalStorageTraceListener.GetLogDirectory( );
#if !DEBUG
                Trace.Listeners.Clear( );
#endif
                _logManager = new LogManager( logDirectory, RoleEnvironment.CurrentRoleInstance.Id );

                // Initialize forecast manager.
                var account = StorageAccount.Current;
                account.Aggregator.ForecastStored += this.OnForecastStored;

                TraceEventType.Verbose.Trace( "Initializing forecast manager..." );
                this._manager = new ForecastManager( account.Aggregator, account.Scheduler, new SystemTimer( ), account.IsPrimaryInstance );

                TraceEventType.Verbose.Trace( "Role initialization complete." );

                return base.OnStart( );
            }
            catch( Exception ex ) {
                ex.Report( "WebRole Startup Failure", wait: true );
                throw;
            }
        }

        public override void OnStop( ) {
            try {
                base.OnStop( );

                StorageAccount.Current.Aggregator.Stop( );

                Trace.Flush( );
                using( _logManager ) { }
            }
            catch( Exception ex ) {
                ex.Report( "WebRole Teardown Failure", wait: true );
                throw;
            }
        }


        private void OnForecastStored( object sender, ForecastEventArgs e ) {
            try {
                // Resolve locations.
                TraceEventType.Information.Trace( "Forecast event received: " + e );
                string specifiedLocation = e.Location.GetName( );
                string actualLocation = e.Try( f => f.Location.GetName( ), specifiedLocation );
                var subscriptionUpdates = Extensions.GetList<Subscription>( empty: specifiedLocation == actualLocation );

                // Get the notification message content.
                var account = StorageAccount.Current;
                string message = e.Try( account.Aggregator.GetUrl, ex => "-" + ex.Message );

                // Notify all subscribers for the specified location.
                RawPushNotificationMessage notification = null;
                foreach( Subscription subscription in account.Subscriptions.Get( specifiedLocation ) ) {
                    subscriptionUpdates.Add( subscription );

                    if( subscription.HasChannelUri ) {
                        // Lazily create notification.
                        if( notification == null )
                            notification = new RawPushNotificationMessage {
                                RawData = Encoding.UTF8.GetBytes( message ),
                                SendPriority = MessageSendPriority.High
                            };

                        notification.SendAsync(
                            subscription.GetChannelUri( ),
                            r => TraceEventType.Verbose.Trace( r.ChannelUri + " message delivery succeeded." ),
                            RemoveOnDeliveryFailure( subscription ) );
                    }
                }

                // If the specified location did not match the actual location, update subscriptions.
                foreach( Subscription subscription in subscriptionUpdates ) {
                    TraceEventType.Information.Trace( "{0} subscription moving from {1} to {2}.",
                        subscription.SerializedChannelUri ?? "Anonymous", e.Location, e.Forecast.Location );
                    account.Subscriptions.Move( subscription, actualLocation );
                }

                Trace.Flush( );
            }
            catch( Exception ex ) {
                ex.Report( "WebRole Forecast Storage Failure", wait: true );
                throw;
            }
        }

        private static Action<MessageSendResult> RemoveOnDeliveryFailure( Subscription subscription ) {
            return delegate( MessageSendResult result ) {
                TraceEventType.Error.Trace( result.ChannelUri + " message delivery failed: " + result.Exception );
                StorageAccount.Current.Subscriptions.Remove( subscription );
            };
        }

    }

}
